package prolog.terms

abstract class Term extends AnyRef with Cloneable {
  def ref = this
  def bind_to(that: Term, trail: Trail): Boolean

  def unify(that: Term, trail: Trail): Boolean
  def getKey(): String = {
    return toString()
  }

  /**
   * Defines the reaction to an agent recursing
   * over the structure of a term. <b>This</b> is passed
   * to the agent and the result of the action is returned.
   * Through overriding, for instance, a Fun term will provide
   * the recursion over its arguments, by applying the action
   * to each of them.
   *
   * @see Fun
   */
  def reaction(agent: Term): Term = agent.action(this)

  /**
   * Identity action.
   */
  def action(that: Term): Term = that

  def copy = reaction(new Copier())

  /*
  def matching_copy(that: Term): Option[Term] = {
    val trail = new Trail();
    val ok = unify(that, trail);
    val res = if (ok) Some(that.copy()) else None
    trail.unwind(0)
    res
  }
  */
  def varsOf(): Term = {
    return (new Copier()).getMyVars(this);
  }

  /**
   * Replaces variables with uppercase constants named
   * `V1', 'V2', etc. to be read back as variables.
   */
  def numbervars(): Term = {
    return copy.reaction(new VarNumberer())
  }

  /**
   * Prints out a term to a String with
   * variables named in order V1, V2,....
   */
  def pprint(): String = {
    return numbervars().toString();
  }

  /*
    Returns an unquoted version of toString()
  */
  def toUnquoted(): String = {
    return pprint();
  }

  /**
   * Java Object wrapper. In particular, it is
   * used to wrap a Thread to hide it inside a Prolog
   * data object.
   */
  def toObject(): Object = {
    return ref;
  }

  final def matches(that: Term, trail: Trail): Boolean = {
    val oldtop = trail.size
    val ok = unify(that, trail)
    trail.unwind(oldtop)
    ok
  }

  final def exec() = -1
  def exec(p: Prog) = -1

  def isBuiltin(): Boolean = {
    return false;
  }

  def toChars(): Nonvar = {
    return stringToChars(toUnquoted());
  }

  final def stringToChars(s: String): Nonvar = {
    if (0 == s.length())
      return Const.aNil; // I added the anil to Const.scala
    var l: Cons = new Cons(new Int_(s.charAt(0)), Const.aNil);
    var curr = l;
    for (i <- 1 until s.length) {
      var tail = new Cons(new Int_(s.charAt(i)), Const.aNil);
      curr.args(1) = tail;
      curr = tail;
    }
    return l;
  }

  def charsToString(Cs: Nonvar): String = {
    var s = new StringBuffer("");
    var Css = Cs
    while (!Css.eq(Nil)) {
      if (!Css.isInstanceOf[Cons])
        return null;
      var head: Nonvar = Css.asInstanceOf[Cons].getArg(0).asInstanceOf[Nonvar];
      if (!head.isInstanceOf[Int])
        return null;
      var c: Char = head.asInstanceOf[Int].asInstanceOf[Char]; // missing the val part
      s.append(c);
      // Cs = (Nonvar)((Cons)Cs).getArg(1);
      Css = Css.asInstanceOf[Cons].getArg(1).asInstanceOf[Nonvar];
    }

    return s.toString();
  }

}

object Term {
  final def copyList(ts: List[Term]): List[Term] =
    {
      val copier = new Copier()
      ts.map(t => t.reaction(copier))
    }

  final val JAVA: Int = -4;
  final val REAL: Int = -3;
  final val INT: Int = -2;
  final val VAR: Int = -1;
  final val CONST: Int = 0;
  /* Not using clause
  def fromString(s:String):Term= {
    return Clause.Clause.clauseFromString(s).toTerm();
  }
  */

  /**
   * Converts a list of character codes to a String.
   */

  /*
  def charsToString(Cs: Nonvar): String = {
    var s = new StringBuffer("");
    var Css = Cs
    while (!Css.eq(Nil)) {
      if (!Css.isInstanceOf[Cons])
        return null;
      var head: Nonvar = Css.asInstanceOf[Cons].getArg(0).asInstanceOf[Nonvar];
      if (!head.isInstanceOf[Int])
        return null;
      var c: Char = head.asInstanceOf[Int].asInstanceOf[Char]; // missing the val part
      s.append(c);
      // Cs = (Nonvar)((Cons)Cs).getArg(1);
      Css = Css.asInstanceOf[Cons].getArg(1).asInstanceOf[Nonvar];
    }

    return s.toString();
  }
*/
}
